#include "serverOp.h"
#include "log.h"
#include "macro.h"
#include "respondMsg.pb.h"
#include "myrsa.h"
#include <fstream>
#include "codecFactory.h"
#include <functional>
#include <ctime>

static sylar::Logger::ptr g_logger=DANIEL_LOG_NAME("system");

ServerOp::ServerOp():m_mysqldb(daniel::MysqlInfo())
{
    m_serverDefine=daniel::ConfigMsg::GetInstance()->lookUp<ServeropDefine>("serveropdefine")->getValue();
    DANIEL_LOG_DEBUG(g_logger)<<"port:"<<m_serverDefine.port<<"ip:"<<m_serverDefine.ip;
    m_server.init(m_serverDefine.port, m_serverDefine.ip);
    daniel::MysqlInfo sqlInfo(m_serverDefine.host,m_serverDefine.user,m_serverDefine.password,m_serverDefine.database,m_serverDefine.dbport);
    m_mysqldb.setMysqlInfo(sqlInfo);
    bool isConnect=m_mysqldb.connectToDB();
    if (isConnect){
        DANIEL_LOG_DEBUG(g_logger)<<"数据库连接成功！"<<std::endl;
    }
    m_threadpool.reset(new daniel::ThreadPool(4,20));
    while (1)
    {
        daniel::TcpSocket* tcpSocket=m_server.acceptConnfd();
        if (tcpSocket==nullptr){
            continue;
        }
        else{
            daniel::TcpSocket::ptr tcpSocketPtr(tcpSocket);
            m_threadpool->addTask(std::bind(&ServerOp::answer,this,tcpSocketPtr));
        }
    }
}

ServerOp::~ServerOp()
{
    m_server.disconnect();
}

void ServerOp::answer(daniel::TcpSocket::ptr tcpSocket)
{
    //接收数据， 反序列化
    std::string recvMsg;

    tcpSocket->recvMsg(recvMsg);

    daniel::Factory::ptr factory(new daniel::RequestFactory(recvMsg));
    daniel::Codec::ptr codec(factory->createObject());
    RequestMsg* request=static_cast<RequestMsg*> (codec->decodeMsg());
    //根据请求的不同，调用不同的函数处理请求
    std::string sendBackStr;
    switch (request->cmdtype())
    {
    case 1:
        sendBackStr=keyNego(*request);
        break;
    case 2:
        keyCheck(*request);
        break;
    case 3:
        keyCancel(*request);
        break;
    default:
        break;
    }
    if (!sendBackStr.empty()){
        DANIEL_LOG_DEBUG(g_logger)<<"sendBackStr:"<<sendBackStr<<std::endl;
        tcpSocket->sendMsg(sendBackStr);
    }
    delete request;
}

/*秘钥协商逻辑：
    1.利用mysqlop提供的结构判断是否是合法站点，合法站点继续，非合法站点，返回错误信息
    2.是合法站点，需要进行公钥校验；校验成功，继续生成对称加密秘钥；
        将收到的公钥，生成的对称加密秘钥，根据站点号插入到数据库中；
    3.将信息使用protobuf生成的respondmsg组织，发回客户端；
*/
std::string ServerOp::keyNego(const RequestMsg& msg)
{
    //将收到的公钥写到磁盘当中
    // std::fstream fs("./serverPublic.pem",std::ios::out);
    // if (fs.is_open()){
    //     fs.write(msg.data().c_str(),msg.data().size());
    //     fs.close();
    // }
    bool isValid=m_mysqldb.isStation(msg.clientid());
    //接收公钥，rsa签名验证
    daniel::MyRsa::ptr rsa(new daniel::MyRsa(daniel::MyRsa::LoadPublicKey,msg.data(),false));
    // rsa->loadKeyFromFile("./serverPublic.pem");
    // DANIEL_LOG_DEBUG(g_logger)<<"after rsa loadKeyFromFile";
    isValid=(rsa->RSA_hashverify(daniel::MyRsa::SignSHA256,msg.data(),msg.sign()))&&isValid;
    DANIEL_ASSERT2(isValid,"station is illigeal or hashverify failed!");
    //生成随机字符串，输入持久化，插入数据库
    RespondMsg respondMsg;
    std::string randStr=generateRandStr(LEN16);
    DANIEL_LOG_DEBUG(g_logger)<<"aes key:"<<randStr;
    //生成respondmsg 发回客户端
    if (!isValid){
        DANIEL_LOG_DEBUG(g_logger)<<"verify failed!"<<std::endl;
        respondMsg.set_rv(false);
    }
    //需要对 对称加密秘钥  进行  公钥加密
    else{
        //将数据插入到数据库
        m_mysqldb.updateKey(msg.clientid(),msg.data(),randStr);
        //将数据序列化，发回客户端
        std::string encryptRandStr;
        rsa->publicEncrypt(randStr,encryptRandStr);
        respondMsg.set_rv(true);
        respondMsg.set_data(encryptRandStr);
        respondMsg.set_seckeyid(1);
        respondMsg.set_clientid(msg.clientid());
        respondMsg.set_serverid(msg.serverid());
    }
    //序列化，发回客户端
    return respondMsg.SerializeAsString();
}

void ServerOp::keyCheck(const RequestMsg& msg)
{

}

void ServerOp::keyCancel(const RequestMsg& msg)
{

}

std::string ServerOp::generateRandStr(AESKeyLen len)
{
    srand(time(nullptr));
    std::string allStr("abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()");
    std::random_shuffle(allStr.begin(),allStr.end());
    return allStr.substr(0,len);
}

struct ServerDefineInit
{
    ServerDefineInit()
    {
        daniel::ConfigMsg::GetInstance()->lookUp("serveropdefine",ServeropDefine(),"serverop configs");
        daniel::ConfigMsg::GetInstance()->loadFromFile("./serverConfig.yml");
    }
};
static ServerDefineInit serverDefineInit;